package org.openzip.openzip.gui.util;

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.*;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;

import org.openzip.openzip.archiving.logic.SelectedFilesModel;
import org.openzip.openzip.gui.util.event.TreeCheckSelectionEvent;
import org.openzip.openzip.gui.util.event.TreeCheckSelectionListener;

import sun.print.PSPrinterJob.EPSPrinter;

public class CheckboxedTree extends JTree
{
	private static final long serialVersionUID = 1L;
	private Collection<TreeCheckSelectionListener> checkListeners;
	private SelectedFilesModel globalSelectionModel;
	private File currentFolder;

	public Collection<Object> getSelectedObjects(){
		throw new UnsupportedOperationException("Not yet implemented.");
	}
	
	public void setSelectedObject(Object o){
		throw new UnsupportedOperationException("Not yet implemented.");
	}
	
	public void setSelectedObjects(Collection<Object> objects){
		throw new UnsupportedOperationException("Not yet implemented.");
	}
	
	public void selectAllObjects(){
		throw new UnsupportedOperationException("Not yet implemented.");
	}
	
	//This is IMHO JTree's method for clearing selection of item in general term
	//Not in terms of unchecking FileTreeNode(s) and therefore should not be
	//used, because we don't know what method's are calling this method.
	//we might for example experience unchecking of all nodes by clicking to a
	//whitespace area of the CheckboxedTree
//	public void clearSelection(){
//		throw new UnsupportedOperationException("Not yet implemented.");
//	}
	
	public void addTreeCheckSelectionListener(
				TreeCheckSelectionListener listener){
		checkListeners.add(listener);
	}
	
	public void removeTreeCheckSelectionListener(
				TreeCheckSelectionListener listener){
		checkListeners.remove(listener);
	}
	
	public CheckboxedTree(File rootFile, boolean directoriesOnly) {
		this(new FileTreeNode(rootFile, directoriesOnly));
	}
	
	private CheckboxedTree(FileTreeNode node){
		super(node);
		checkListeners = new HashSet<TreeCheckSelectionListener>();
		setCellRenderer(new FileTreeNodeCellRenderer());
		addMouseListener(new FileTreeNodeMouseListener());
		getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		addTreeSelectionListener(new SelfSelectionListener());
	}
	
	protected void fireTreeCheckSelectionChanged(TreePath path, int oldState, int state){
		TreeCheckSelectionEvent event = new TreeCheckSelectionEvent(path, this, oldState, state);
		for(TreeCheckSelectionListener listener : checkListeners){
			listener.treeCheckSelectionChanged(event);
		}
	}
	
	public File getCurrentFolder(){
		return currentFolder;
	}
	
	public void scrollToFile(File f){
		if(f.isFile()){
			f = f.getParentFile();
		}
		String[] paths = FileUtil.splitFilePath(f);
		/*for(int i = 0; i < paths.length; i++){
			System.out.println(paths[i]);
		}*/
		int position = 0;
		for(int i = 0; i < paths.length; i++){
			//System.out.println("Searching for: " + paths[i]);
			String s = "";
			for(;!s.equals(paths[i]);position++){
				s = ((FileTreeNode)getPathForRow(position).
							getLastPathComponent()).getFilePathLast();
				//System.out.println("This is " + s);
			}
			if(i != paths.length - 1){
				expandRow(position-1);
			}
		}
		setSelectionRow(position-1);
	}

	/**
	 * 
	 * @deprecated This method is deprecated. Check selection state is held by an 
	 * instance of SelectedFilesModel. Changes in check selection can be listened
	 * to via TreeCheckSelectionListener.
	 */
	@Deprecated public Collection<File> getSelectedFiles() {

		Collection<File> filesCollection;
		filesCollection = new ArrayList<File>();
		
		FileTreeNode root = (FileTreeNode)this.getModel().getRoot();
		if (root.getFileCheckBox().isSelected()) {
			recursion(root);
		}

		
		return filesCollection;
	}
	
	//This method should be renamed to better express its purpose
	//It recursively takes all children of parentNode and those which are
	//checked(FileCheckBox) are added to filesCollection attribute
	
	//there is problem with accessing some files - namely, so far:
	//LocalSettings/History
	//http://lists.sans.org/pipermail/list/2002-October/005692.html
	//
	//FileSystemView.getShellFolder: f=C:\Documents and Settings\Synthetic\Local Settings\History\desktop.ini
	//java.io.FileNotFoundException: File C:\Documents and Settings\Synthetic\Local Settings\History\desktop.ini not found
	private void recursion(FileTreeNode parentNode) {
		
		Enumeration<FileTreeNode> e = parentNode.children();
		FileTreeNode node;
		while (e.hasMoreElements()) {
			node = (FileTreeNode)e.nextElement();
			if (node.getFileCheckBox().isSelected()) {
				System.out.println(node.getFileCheckBox().getFile());
				//filesCollection.add(node.getFileCheckBox().getFile());
				recursion(node);
			}
			
		}
		
	}
	
	private class SelfSelectionListener implements TreeSelectionListener
	{
		@Override public void valueChanged(TreeSelectionEvent e){
			FileTreeNode node = 
				(FileTreeNode)e.getPath().getLastPathComponent();
			currentFolder = node.getFileCheckBox().getFile();
		}
	}
	
	public class FileTreeNodeMouseListener extends MouseAdapter {
		
		/**
		 * This method will cause the selected checkbox go the opposite state. 
		 * (Selected if unselected, or unselected if selected).  
		 */
		@Override 
		public void mouseClicked(MouseEvent e){
			 
			 int x = e.getX();
			 int y = e.getY();
			 
			 JTree tree = (JTree)e.getComponent();
			 TreePath path = tree.getPathForLocation(x, y);
			 if (path == null) {
				 // click outside any node
				 return;
			 }
			 FileTreeNode node = (FileTreeNode)path.getLastPathComponent();
			 FileCheckBox cb = (FileCheckBox)node.getFileCheckBox();
			 if((e.getModifiers() & MouseEvent.CTRL_MASK) == 2) {

			 	cb.nextState();
			 	fireTreeCheckSelectionChanged(path, 0, 0);
			 	
			 	//now, we are adding/removing the file represented by clicked
			 	//checkbox to/from the global selection model
			 	
			 	//Carefully here: checkbox's state has already been changed to
			 	//reflect user's click on it. Therefore SELECTED means that
			 	//checkbox has been UNSELECTED and user SELECTED it.
			 	if (cb.getState() == State.SELECTED) {
			 		if (cb.getFile() == null ) {
			 			System.out.println("null file!!!");
			 			return;
			 		}

			 		globalSelectionModel.addFile(cb.getFile());
			 	}
			 	//USER cannot set state as UNDECIDED and therefore we assume
			 	//the other state being UNSELECTED
			 	//This means we want to remove File from our global selection
			 	//model
			 	else {
			 		globalSelectionModel.removeFile(cb.getFile());
			 	}
			 	
			 }
			 tree.repaint();
		}
		
	}
	
	public class FileTreeNodeCellRenderer implements TreeCellRenderer {
		
		@Override
		public Component getTreeCellRendererComponent(JTree tree, Object value,
				boolean sel, boolean expanded, boolean leaf, int row,
				boolean hasFocus) {
			
			JCheckBox val = ((FileTreeNode)value).getFileCheckBox();
			
			if (sel) {
				val.setBackground(new Color(127, 127, 255));
			}
			else {
				val.setBackground(tree.getBackground());
			}

			return val;
		}
		
	}
	
	public SelectedFilesModel getGlobalSelectionModel() {
		return this.globalSelectionModel;
	}
	
	public void setGlobalSelectionModel(SelectedFilesModel model) {
		this.globalSelectionModel = model;
	}
	
	public static void main(String[] args) {
		JFrame frame = new JFrame("Checkboxed Tree Test");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		
		CheckboxedTree tree = new CheckboxedTree(new File("C:\\"), false);
		
		frame.add(new JScrollPane(tree), BorderLayout.WEST);
		
		final CheckboxedTree t = tree;
		
		JButton button = new JButton("Print Selected");
		button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				
				for (File file : t.getSelectedFiles()) {
					System.out.println(file.getName());
				}
				
			}
			
		});
		
		frame.add(button, BorderLayout.EAST);
				
		frame.pack();
		frame.setVisible(true);
	}
}
